* use QRegularExpression for xml generic tag matching.
The kml reader now creates tracks from innerBoundryIs elements in
addition to the historic creation from outerBoundryIs elements.
* collapse kml 2.3 track elements
* const correctness improvements for xmlgeneric.
return s1.left(n).compare(s2.left(n), Qt::CaseInsensitive);
}
-int str_match(const char* str, const char* match);
-
[[gnu::format(printf, 2, 3)]] int xasprintf(char** strp, const char* fmt, ...);
[[gnu::format(printf, 2, 3)]] int xasprintf(QString* strp, const char* fmt, ...);
[[gnu::format(printf, 2, 3)]] int xasprintf(QScopedPointer<char, QScopedPointerPodDeleter>& strp, const char* fmt, ...);
path = nullptr;
}
-static xg_tag_mapping gdx_map[] = {
+static QList<xg_tag_mapping> gdx_map = {
{ device_s, cb_cdata, "/Device/Model/Description" },
{ id_s, cb_cdata, "/Device/Id" },
{ path_s, cb_cdata, "/Device/MassStorageMode/DataType/File/Location/Path" },
{ ext_s, cb_cdata, "/Device/MassStorageMode/DataType/File/Location/FileExtension" },
{ base_s, cb_cdata, "/Device/MassStorageMode/DataType/File/Location/BaseName" },
{ dir_s, cb_cdata, "/Device/MassStorageMode/DataType/File/TransferDirection" },
- { nullptr, (xg_cb_type) 0, nullptr }
};
const gdx_info*
{&KmlFormat::wpt_time, cb_cdata, "/Placemark/TimeStamp/when"},
// Alias for above used in KML 2.0
{&KmlFormat::wpt_time, cb_cdata, "/Placemark/TimeInstant/timePosition"},
- {&KmlFormat::wpt_coord, cb_cdata, "/Placemark/Point/coordinates"},
- {&KmlFormat::wpt_coord, cb_cdata, "/Placemark/MultiGeometry/Point/coordinates"},
+ {&KmlFormat::wpt_coord, cb_cdata, "/Placemark/(.+/)?Point/coordinates"},
{&KmlFormat::wpt_icon, cb_cdata, "/Placemark/Style/Icon/href"},
- {&KmlFormat::trk_coord, cb_cdata, "/Placemark/MultiGeometry/LineString/coordinates"},
- {&KmlFormat::trk_coord, cb_cdata, "/Placemark/GeometryCollection/LineString/coordinates"},
- {&KmlFormat::trk_coord, cb_cdata, "/Placemark/Polygon/outerBoundaryIs/LinearRing/coordinates"},
- {&KmlFormat::trk_coord, cb_cdata, "/Placemark/LineString/coordinates"},
- {&KmlFormat::gx_trk_s, cb_start, "/Placemark/*gx:Track"},
- {&KmlFormat::gx_trk_e, cb_end, "/Placemark/*gx:Track"},
- {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/*gx:Track/when"},
- {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/*gx:Track/gx:coord"},
- {&KmlFormat::gx_trk_s, cb_start, "/Placemark/Track"}, // KML 2.3
- {&KmlFormat::gx_trk_e, cb_end, "/Placemark/Track"}, // KML 2.3
- {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/Track/when"}, // KML 2.3
- {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/Track/coord"}, // KML 2.3
- {&KmlFormat::gx_trk_s, cb_start, "/Placemark/MultiTrack/Track"}, // KML 2.3
- {&KmlFormat::gx_trk_e, cb_end, "/Placemark/MultiTrack/Track"}, // KML 2.3
- {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/MultiTrack/Track/when"}, // KML 2.3
- {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/MultiTrack/Track/coord"} // KML 2.3
+ {&KmlFormat::trk_coord, cb_cdata, "/Placemark/(.+/)?LineString/coordinates"},
+ {&KmlFormat::trk_coord, cb_cdata, "/Placemark/(.+)/?LinearRing/coordinates"},
+ {&KmlFormat::gx_trk_s, cb_start, "/Placemark/(.+/)?gx:Track"},
+ {&KmlFormat::gx_trk_e, cb_end, "/Placemark/(.+/)?gx:Track"},
+ {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/(.+/)?gx:Track/when"},
+ {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/(.+/)?gx:Track/gx:coord"},
+ {&KmlFormat::gx_trk_s, cb_start, "/Placemark/(.+/)?Track"}, // KML 2.3
+ {&KmlFormat::gx_trk_e, cb_end, "/Placemark/(.+/)?Track"}, // KML 2.3
+ {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/(.+/)?Track/when"}, // KML 2.3
+ {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/(.+/)?Track/coord"}, // KML 2.3
};
// The TimeSpan/begin and TimeSpan/end DateTimes:
return outsize;
}
-/*
- * compare str with match
- * match may contain wildcards "*" and "?"
- *
- * examples:
- * str_match("ABCDE", "*BC*") -> 1
- * str_match("ABCDE", "A*C*E") -> 1
- * str_match("?ABCDE", "\\?A*") -> 1
- * str_match("", "*A") -> 0
- */
-
-int
-str_match(const char* str, const char* match)
-{
- const char* s = str;
- const char* m = match;
-
- while (*m || *s) {
- switch (*m) {
-
- case '\0':
- /* there is something left in s, FAIL */
- return 0;
-
- case '*':
- /* skip all wildcards */
- while ((*m == '*') || (*m == '?')) {
- m++;
- }
- if (*m == '\0') {
- return 1;
- }
-
- if (*m == '\\') { /* ? escaped ? */
- m++;
- if (*m == '\0') {
- return 0;
- }
- }
-
- do {
- while (*s && (*s != *m)) {
- s++;
- }
- if (*s == '\0') {
- return 0;
- }
-
- const char* sx = s + 1;
- const char* mx = m + 1;
-
- while (*sx) {
- if (*mx == '\\') { /* ? escaped ? */
- mx++;
- if (*mx == '\0') {
- return 0;
- }
-
- }
- if (*sx == *mx) {
- sx++;
- mx++;
- } else {
- break;
- }
- }
- if (*mx == '\0') { /* end of match */
- if (*sx == '\0') {
- return 1;
- }
- s++;
- } else if ((*mx == '?') || (*mx == '*')) {
- s = sx;
- m = mx;
- break;
- } else {
- s++;
- }
- } while (*s);
- break;
-
- case '?':
- if (*s == '\0') {
- return 0; /* no character left */
- }
- m++;
- s++;
- break;
-
- case '\\':
- m++;
- if (*m == '\0') {
- return 0; /* incomplete escape sequence */
- }
- /* pass-through next character */
- [[fallthrough]];
-
- default:
- if (*m != *s) {
- return 0;
- }
- m++;
- s++;
- }
- }
- return ((*s == '\0') && (*m == '\0'));
-}
-
void
printposn(const double c, bool is_lat)
{
xg_shortcut_ignore
};
-static QList<xg_tag_map_entry>* xg_tag_tbl;
+static const QList<xg_tag_map_entry>* xg_tag_tbl;
static bool dynamic_tag_tbl;
static QHash<QString, xg_shortcut>* xg_shortcut_taglist;
static XgCallbackBase*
xml_tbl_lookup(const QString& tag, xg_cb_type cb_type)
{
- const QByteArray key = tag.toUtf8();
- const char* keyptr = key.constData();
- for (const auto& tm : qAsConst(*xg_tag_tbl)) {
- if ((cb_type == tm.cb_type) && str_match(keyptr, tm.tag_name)) {
- return tm.tag_cb;
+ for (const auto& tm : *xg_tag_tbl) {
+ if (cb_type == tm.cb_type) {
+ QRegularExpressionMatch match = tm.tag_re.match(tag);
+ if (match.hasMatch()) {
+ return tm.tag_cb;
+ }
}
}
return nullptr;
}
void
-xml_init(const QString& fname, QList<xg_tag_map_entry>* tbl, const char* encoding,
+xml_init(const QString& fname, const QList<xg_tag_map_entry>* tbl, const char* encoding,
const char* const* ignorelist, const char* const* skiplist, bool dynamic_tbl)
{
xg_tag_tbl = tbl;
}
void
-xml_init(const QString& fname, xg_tag_mapping* tbl, const char* encoding,
+xml_init(const QString& fname, const QList<xg_tag_mapping>& tbl, const char* encoding,
const char* const* ignorelist, const char* const* skiplist)
{
- xg_tag_tbl = new QList<xg_tag_map_entry>;
+ auto* tag_tbl = new QList<xg_tag_map_entry>;
dynamic_tag_tbl = true;
- for (xg_tag_mapping* tm = tbl; tm->tag_cb != nullptr; ++tm) {
- auto* cb = new XgFunctionPtrCallback(tm->tag_cb);
- xg_tag_tbl->append({cb, tm->cb_type, tm->tag_name});
+ for (const auto& tm : tbl) {
+ auto* cb = new XgFunctionPtrCallback(tm.tag_cb);
+ QRegularExpression re(QRegularExpression::anchoredPattern(tm.tag_pattern));
+ assert(re.isValid());
+ tag_tbl->append({cb, tm.cb_type, re});
}
+ xg_tag_tbl = tag_tbl;
xml_common_init(fname, encoding, ignorelist, skiplist);
}
xml_deinit()
{
if (dynamic_tag_tbl) {
- for (const auto& tm : qAsConst(*xg_tag_tbl)) {
+ for (const auto& tm : *xg_tag_tbl) {
delete tm.tag_cb;
}
delete xg_tag_tbl;
#ifndef XMLGENERIC_H_INCLUDED_
#define XMLGENERIC_H_INCLUDED_
-#include <QString> // for QString
-#include <QXmlStreamAttributes> // for QXmlStreamAttributes
+#include <cassert> // for assert
+
+#include <QList> // for QList
+#include <QRegularExpression> // for QRegularExpression
+#include <QString> // for QString
+#include <QXmlStreamAttributes> // for QXmlStreamAttributes
+
// Maybe the XmlGeneric string callback really shouldn't have a type
// of its own; this was a crutch during the move from char* to QString.
struct xg_tag_map_entry {
XgCallbackBase* tag_cb;
xg_cb_type cb_type;
- const char* tag_name;
+ QRegularExpression tag_re;
};
// Table generation from an array containing function pointers.
struct xg_tag_mapping {
xg_callback* tag_cb;
xg_cb_type cb_type;
- const char* tag_name;
+ const char* tag_pattern;
};
// Table generation from a list containing member function pointers.
using XgCb = void (MyFormat::*)(xg_string, const QXmlStreamAttributes*);
XgCb tag_cb;
xg_cb_type cb_type;
- const char* tag_name;
+ const char* tag_pattern;
};
template<class MyFormat, typename my_functor_map_entry>
QList<xg_tag_map_entry>* build_xg_tag_map(MyFormat* instance, const QList<my_functor_map_entry>& map)
{
auto* tag_tbl = new QList<xg_tag_map_entry>;
- for (const auto& entry : qAsConst(map)) {
+ for (const auto& entry : map) {
auto* tag_cb = new XgFunctor<MyFormat>(instance, entry.tag_cb);
- tag_tbl->append({tag_cb, entry.cb_type, entry.tag_name});
+ QRegularExpression re(QRegularExpression::anchoredPattern(entry.tag_pattern));
+ assert(re.isValid());
+ tag_tbl->append({tag_cb, entry.cb_type, re});
}
return tag_tbl;
}
* attempt to free the table resources when xml_deinit is called.
*
* 2. Have xml_init build and own a table of XgFunctionPtrCallback entries
- * from an array of function pointers, i.e. a xg_tag_mapping array.
+ * from an list of function pointers, i.e. a QList of xg_tag_mapping elements.
* This only works when all callbacks are function pointers.
* xml_init(fname, tbl, encoding, ignorelist, skiplist);
* Generated table entries will automatically be freed.
* resources when xml_deinit is called.
*
*/
-void xml_init(const QString& fname, QList<xg_tag_map_entry>* tbl, const char* encoding,
+void xml_init(const QString& fname, const QList<xg_tag_map_entry>* tbl, const char* encoding,
const char* const* ignorelist = nullptr,
const char* const* skiplist = nullptr, bool dynamic_tbl = false);
-void xml_init(const QString& fname, xg_tag_mapping* tbl,const char* encoding,
+void xml_init(const QString& fname, const QList<xg_tag_mapping>& tbl,const char* encoding,
const char* const* ignorelist = nullptr,
const char* const* skiplist = nullptr);
void xml_read();